iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 4
1
Software Development

每天 Racket 3 分鐘系列 第 6

(if day-05 "來杯咖啡談是非 — Racket 的其他型態" (void))

  • 分享至 

  • xImage
  •  

1. 是非無關對錯

Racket 的 true 與 false 不若 Java、C# 與 Python,是以完整的字來表示,從字元那節開始,我們已經開始接觸 Racket 的一些特殊表示方式,現在所談的布林值,也是如此。Racket 以 #t 當作 true,#f 當作 false。而像 C 語言那樣,非布林的值也有布林含意。但 Racket 裡,只有 #f 可以代表 false,其他非 #f 的任何值,與 #t 皆為 true。當然這是一項不太好的設計,但每個語言多少有個一兩項讓人扼腕的地方,這是很正常的事。換句話說,你可以把 '() (空 list)帶進 if 區塊中,這在 Racket 是合理的,但在 Java 與 C# 裡,會連 compiler 都過不了。

因此,我們可以回到之前所說的型別判斷:

(string? "a")  ;; #t
(string? #\a)  ;; #f

(exact? 3.14)    ;; #f
(inexact? 3.14)  ;; #t

2. String 的另一個形式

我們在大多數語言裡,都可以把 String 轉換成 byte array,有時候比較麻煩,像 C# 要呼叫 Encoding,Java 直接 getBytes()。然而在 Racket 裡頭,另有一種 String,稱為 Byte String,不若 String 是以字元 list 的形式儲存,Byte String 是以 byte list 的形式儲存,因此它可以處理更底層的 String 內容。Byte String 的表示法,就是在 String 前方加上 #

我們在 CJK 字集的國家裡,處理文字時長度時,通常需要先區別要處理的是 字數 還是 位元組數 這兩種不同的長度。因此 Racket 的 Byte String 提供了一個轉換機制:

(define str "我是字串")
(string-length str)   ;; 4

(define bstr (string->bytes/utf-8 str))
(bytes-length bstr)   ;; 12

然而,不只處理文字。在 Racket 裡,表示二進位資料也是用 bytes 的形式(對比 Scheme 則是使用 Byte Vector),後續有機會說到一些實務應用與 IO 時,會再回頭與它相遇。

3. 來點抽象符號思考

至此,所談到的資料型態,都是很基本的型態,不管是數值、字串、布林、bytes。現在我們要進入的是這門語言資料型態較為抽象的一層。Racket 裡有兩種相當抽象的資料型態 — Symbol 與 Keyword。

Symbol 中文翻作符號,這像什麼呢?如果各位有 Java 或 C# 之類高階物件導向語言的經驗,它類似 Java 裡的 enum 型態。Symbol 使用單引號來表示:'a-symbol

根據 Racket Guide [1],Symbol 是一種 interned 的資料,什麼是 interned?我查了字典,可作實習生(當然,怎麼可能!),亦可做拘押;Racket Guide 所給的提示實在有限;或許看看規格書怎麼說,Scheme 規格書僅僅簡單地描述了 Symbol 在比較時的特性:當兩個 Symbol 的字母拼法完全一致時,這兩個可謂完全相等 [2];最後,我參考了 Scheme 最權威的教科書之一:TSPL4,其中題到了非常重要的關鍵:Symbol 在實作時,是一種查表對應的機制,名字相同的 Symbol,其實作上,就是參考到同一個記憶體物件,因此在進行比較時,可以非常迅速。相較起來, String(字串)比較,則需逐一巡訪字串裡每一個字,效率並不高。 [3]因此,我們可以知道,在 Racket 的 interned 這個字,應該就是指 Symbol 在建立時,會被 "關" 在某個表裡。

因為在 Racket 可以做以下操作,將 String 轉換為 Symbol(將 String 關起來):

(define a-symb 'a-symbol)
(define another-symb (string->symbol "a-symbol"))
(eq? a-symb another-symb)

當然,你可以把它轉回來,但 Symbol 不等於 String:

(define a-str (symbol->string a-symb))
(string? a-symb)  ;; #f
(symbol? a-str)   ;; #f
(eq? a-symb a-str)  ;; #f

然而,Racket 有個 Scheme 沒有的特殊型態:Keyword。這個可以看一下範例:

(define fruites "apple banana graple")
(define fruite-list (string-split fruites #:trim? #t))
fruite-list

我們先定義一個長字串,然後分割它,在大多數語言裡,string splitting 是很平常的事。然而,假設 Java 有這樣的 method,string splitting 時,還要把每個字串的前後空白都 trim 掉,method 會怎麼宣告呢?

public String[] split(String sep, boolean trim){}

但在 Racket 裡頭,提供了一種為參數命名的機制,就是使用 Keyword 型態,它在記憶體裡也是以一個字串的形式來儲存,但字面表達上是在前方加上:#:。Keyword 型態很少單獨使用,絕大多數是拿來當作函式呼叫的參數別名使用,我們便不在此深入討論。

4. 看不見,可是依舊存在的註解

註解在 Racket 裡頭有兩種形式,其一就是常常在我們範例程式出現的,以 ; 為首的單行註解,類似 Java、C/C++/C# 的 //。另外還有整段文字式的:#| 註解內容 |#,這個就可以進行多行的註解,類似 Java、C/C++/C# 的 /* */

因此,各位在以上、以下的範例程式裡,會看見我使用註解以表達輸出訊息或進一步的說明。


上一篇
(define day-04 ""Hello world!" 怎麼這時候才出現!— Racket 的字串型態")
下一篇
(display "λf.(λx.f (x x)) (λx.f (x x)) — day-06 — Racket 的 Function 與 Lambda — 1")
系列文
每天 Racket 3 分鐘17
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言